using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SmallMvc
{
    public class Element
    {
        private readonly IList<string> _attributeNames = new List<string>();
        private readonly IList<string> _attributeValues = new List<string>();
        private object[] _children;

        public Element()
        {
            Render = true;
        }

        protected string Name
        {
            get { return GetType().Name.ToLowerInvariant(); }
        }

        protected bool Render { get; set; }

        protected void AddAttribute(string name, string value)
        {
            _attributeNames.Add(name);
            _attributeValues.Add(value);
        }

        protected string GetUnclosedString()
        {
            var rawResult = new StringBuilder("<").Append(Name);
            for (var index = 0; index < _attributeNames.Count; index++)
            {
                rawResult.AppendFormat(" {0}=\"{1}\"", _attributeNames[index], _attributeValues[index]);
            }
            return rawResult.ToString();
        }

        public Element If(bool render)
        {
            Render = render;
            return this;
        }

        public Element IfChildren()
        {
            If(_children.Length > 0);
            return this;
        }

        public Element _(params object[] children)
        {
            _children = children;
            return this;
        }

        public Element _<T>(Func<T, object> func, params T[] children)
        {
            return AssignChildrenFromFunc(func, children);
            /*_children = new object[children.Length];
            for (var index = 0; index < children.Length; index++)
            {
                _children[index] = func.Invoke(children[index]);
            }
            return this;*/
        }

        public Element _<T>(Func<T, object> func, IEnumerable<T> children)
        {
            return AssignChildrenFromFunc(func, children);
        }

        private Element AssignChildrenFromFunc<T>(Func<T, object> func, IEnumerable<T> children)
        {
            var childList = children.ToList();
            _children = new object[childList.Count];
            for (var index = 0; index < childList.Count; index++)
            {
                _children[index] = func.Invoke(childList[index]);
            }
            return this;
        }

        public override string ToString()
        {
            if (!Render) return string.Empty;
            if (_children == null || _children.Length == 0)
            {
                return GetUnclosedString() + "/>";
            }
            var rawResult = new StringBuilder();

            rawResult.Append(GetUnclosedString()).Append(">").AppendLine();
            RenderChildren(rawResult);
            rawResult.AppendFormat("</{0}>", Name);
            return rawResult.ToString();
        }

        protected void RenderChildren(StringBuilder rawResult)
        {
            foreach (var child in _children)
            {
                rawResult.Append(child.ToString().Indent());
            }
        }

        public static string operator +(Element e1, Element e2)
        {
            return e1.ToString() + e2;
        }

        public static implicit operator string(Element e)
        {
            return e.ToString();
        }

    }
}